import axios from 'axios';
import { Md5 as md5 } from 'ts-md5/dist/md5';
import {
    requestInterceptors,
    responseInterceptors,
} from './interceptors';

const timeGroup = Object.create(null);

axios.defaults.timeout = 10000;
// 装载所有 request interceptor
requestInterceptors.forEach((item:any) => {
    axios.interceptors.request.use(...item);
});
// 加载所有 response interceptor
responseInterceptors.forEach((item:any) => {
    axios.interceptors.response.use(...item);
});

interface Url {
    url: string;
    baseURL: string;
}
interface Config extends Url {
    method: string;
}
type Option = {
    isCache: boolean;
    delayed: number;
    isDebounce: boolean;
    isDebounceParams: boolean;
    debounceDelayed: number;
}
type Params = { 
    method: string, 
    params?:  Param, 
    option?: Option
}
type Param = { 
    [key: string]: any 
}
interface Query {
    x: number;
    y: string;
    z: boolean;
}
type QueryParam = { 
    [key in keyof Query]: Query[key] 
}

class Resource {
    private readonly _url: string;
    private readonly _baseURL: string;
    private requestUrl: string;
    private options: Option;
    private param: string;
    private key: string;
    private now: number;
    constructor({ url, baseURL }: Url) {
        this._url = url;
        this._baseURL = baseURL;

        this.requestUrl = '';
        this.param = '?';
        this.options = {            
            isCache: false, // 是否缓存数据
            delayed: 60, // 缓存数据时长(秒)
            isDebounce: false, // 是否接口防抖
            isDebounceParams: true, // 接口防抖时是否增加参数保证键值唯一
            debounceDelayed: 800, // 接口防抖时长(毫秒)
        };
        this.key = '';
        this.now = 0;
    }

    stream({ method, params, option }:Params = { method: 'get', params: undefined, option: undefined }) {
        const config:Config = {
            method: method.toLowerCase(),
            baseURL: this._baseURL,
            url: this._url,
        };
        this.options = Object.assign(this.options, option);
        this.requestUrl = this._baseURL + this._url;
        this.now = new Date().getTime() / 1000;
        for (const i in params ) {
            this.param += `${i}=${params[i]}&`;
        }

        // 判断是否缓存
        if (this.options.isCache) {
            const key = this.options.isDebounceParams
					  ? JSON.stringify(this.requestUrl + this.param.replace(/[&?]$/, ''))
					  : JSON.stringify(this.requestUrl)
            this.key = md5.hashStr(key).toString();
            const Cache = JSON.parse(<string> localStorage.getItem(this.key));
            if (Cache) {
                // 判断是否超时以进行缓存
                const timeout = (Cache.time + this.options.delayed) < this.now;
                if (!timeout) {
                    return Cache.cacheData;
                }
            }
        }

        // 判断是否防抖
        return new Promise(async (resolve, reject) => {
            if (this.options.isDebounce) {
                const key = this.options.isDebounceParams
						  ? JSON.stringify(this.requestUrl + this.param.replace(/[&?]$/, ''))
						  : JSON.stringify(this.requestUrl)
                const md5key = md5.hashStr(key).toString();
                clearTimeout(timeGroup[md5key]);
                timeGroup[md5key] = setTimeout(async () => {
                    const data = await this.createRequest(config);
                    resolve(data);
                }, this.options.debounceDelayed);
            } else {
                const data = await this.createRequest(config);
                resolve(data);
            }
        });
    }

    private async createRequest(config: any) {
        const data = await axios.request(config);
        // 缓存数据
        if (this.options.isCache) {
            localStorage.setItem(this.key, JSON.stringify({
                time: this.now,
                cacheData: data,
            }));
        }

        return data;
    }

    GET(params: object) {
        return axios.request({
            method: 'get',
            baseURL: this._baseURL,
            url: this._url,
            params,
        });
    }

    POST(params: object) {
        return axios.request({
            method: 'post',
            baseURL: this._baseURL,
            url: this._url,
            data: params,
        });
    }

    UPLOAD(params: object) {
        const formData = new FormData();
        for (const key in params) {
            // @ts-ignore
            formData.append(key, params[key]);
        }
        return axios.request({
            method: 'post',
            baseURL: this._baseURL,
            url: this._url,
            data: formData,
            headers: { 'Content-Type': 'application/x-www-form-urlencoded;' },
            onUploadProgress(progressEvent) {
                console.log('上传进度=>>>', progressEvent);
            },
        });
    }
}

// 副接口拼接
const stream = (URL:string, baseURL:string) => (url = '') => {
    const path:string = url ? (URL + url) : URL;
    return new Resource({url: path, baseURL: baseURL});
};

export default stream;
